baubs git
Commit e8f5c9085cd3404916070e86bafaa395ad7c5fbd
Parents : 7fd31d2
Author : Jan-Henrik Bruhn <jan-henrik.bruhn@offis.de>
Date : 2025-12-26T21:49:58+01:00
fix: Refactor useCanvasViewport to eliminate ESLint hook warnings
Removed disabled ESLint rules by refactoring state management to follow React best practices. Changes include:
- Replaced setState-in-effect pattern with state-during-render pattern for viewport resets
- Changed from ref-based to state-based storage for initialScale to avoid ref updates during render
- Implemented React-recommended pattern for deriving state from props (similar to getDerivedStateFromProps)
- Properly track pattern changes in state to detect when viewport should reset
This eliminates the need for ESLint disable comments while maintaining the same functionality. The viewport now correctly resets when patterns change, using a pattern that React explicitly recommends for this use case.
Fixes #30
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes
Diff
diff --git a/src/hooks/useCanvasViewport.ts b/src/hooks/useCanvasViewport.ts
index b300945..c8e4727 100644
--- a/src/hooks/useCanvasViewport.ts
+++ b/src/hooks/useCanvasViewport.ts
@@ -5,13 +5,7 @@
* Handles wheel zoom and button zoom operations
*/
-import {
- useState,
- useEffect,
- useCallback,
- useRef,
- type RefObject,
-} from "react";
+import { useState, useEffect, useCallback, type RefObject } from "react";
import type Konva from "konva";
import type { PesPatternData } from "../formats/import/pesImporter";
import type { MachineInfo } from "../types/machine";
@@ -34,8 +28,11 @@ export function useCanvasViewport({
const [stagePos, setStagePos] = useState({ x: 0, y: 0 });
const [stageScale, setStageScale] = useState(1);
const [containerSize, setContainerSize] = useState({ width: 0, height: 0 });
- const initialScaleRef = useRef<number>(1);
- const prevPesDataRef = useRef<PesPatternData | null>(null);
+ const [initialScale, setInitialScale] = useState(1);
+
+ // Track the last processed pattern to detect changes during render
+ const [lastProcessedPattern, setLastProcessedPattern] =
+ useState<PesPatternData | null>(null);
// Track container size with ResizeObserver
useEffect(() => {
@@ -59,41 +56,36 @@ export function useCanvasViewport({
return () => resizeObserver.disconnect();
}, [containerRef]);
- // Calculate and store initial scale when pattern or hoop changes
- useEffect(() => {
- // Use whichever pattern is available (uploaded or original)
- const currentPattern = uploadedPesData || pesData;
- if (!currentPattern || containerSize.width === 0) {
- prevPesDataRef.current = null;
- return;
- }
-
- // Only recalculate if pattern changed
- if (prevPesDataRef.current !== currentPattern) {
- prevPesDataRef.current = currentPattern;
-
- const { bounds } = currentPattern;
- const viewWidth = machineInfo
- ? machineInfo.maxWidth
- : bounds.maxX - bounds.minX;
- const viewHeight = machineInfo
- ? machineInfo.maxHeight
- : bounds.maxY - bounds.minY;
-
- const initialScale = calculateInitialScale(
- containerSize.width,
- containerSize.height,
- viewWidth,
- viewHeight,
- );
- initialScaleRef.current = initialScale;
-
- // Reset view when pattern changes
- // eslint-disable-next-line react-hooks/set-state-in-effect
- setStageScale(initialScale);
- setStagePos({ x: containerSize.width / 2, y: containerSize.height / 2 });
- }
- }, [pesData, uploadedPesData, machineInfo, containerSize]);
+ // Reset viewport when pattern changes (during render, not in effect)
+ // This follows the React-recommended pattern for deriving state from props
+ const currentPattern = uploadedPesData || pesData;
+ if (
+ currentPattern &&
+ currentPattern !== lastProcessedPattern &&
+ containerSize.width > 0
+ ) {
+ const { bounds } = currentPattern;
+ const viewWidth = machineInfo
+ ? machineInfo.maxWidth
+ : bounds.maxX - bounds.minX;
+ const viewHeight = machineInfo
+ ? machineInfo.maxHeight
+ : bounds.maxY - bounds.minY;
+
+ const newInitialScale = calculateInitialScale(
+ containerSize.width,
+ containerSize.height,
+ viewWidth,
+ viewHeight,
+ );
+
+ // Update state during render when pattern changes
+ // This is the recommended React pattern for resetting state based on props
+ setLastProcessedPattern(currentPattern);
+ setInitialScale(newInitialScale);
+ setStageScale(newInitialScale);
+ setStagePos({ x: containerSize.width / 2, y: containerSize.height / 2 });
+ }
// Wheel zoom handler
const handleWheel = useCallback((e: Konva.KonvaEventObject<WheelEvent>) => {
@@ -159,10 +151,9 @@ export function useCanvasViewport({
}, [containerSize]);
const handleZoomReset = useCallback(() => {
- const initialScale = initialScaleRef.current;
setStageScale(initialScale);
setStagePos({ x: containerSize.width / 2, y: containerSize.height / 2 });
- }, [containerSize]);
+ }, [initialScale, containerSize]);
return {
// State
Served by rngit 1.3.3 - Generated in 0.05s